package com.pumpkin.web.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.pumpkin.web.exception.AppServerException;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * (description)
 *
 * @author jinlong.liao on 2016/6/30
 */
public class Result {

    private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static int DEFAULT_SUCCESS = 0;

    private static int DEFAULT_FAIL = -1;

    private int resultCode;     // 结果代码，通常 0 表示成功

    private String message;      // 相关信息，通常用于展示错误信息

    private Map<String, Object> data;   // 返回值

    private Throwable exception;        // 相关异常信息

    ////////////////////////////////////////////////////////////////

    public Result() {
    }

    public Result(int resultCode, String message) {
        this.resultCode = resultCode;
        this.message = message;
    }

    public Result(int resultCode, String message, Throwable exception) {
        this.resultCode = resultCode;
        this.message = message;
        this.exception = exception;
    }

    /**
     * 获得缺省的失败代码，注意这个设置是全局的
     *
     * @return 缺省的失败代码
     */
    public static int getDefaultFail() {
        return DEFAULT_FAIL;
    }

    /**
     * 获得缺省的失败代码，注意这个设置是全局的
     *
     * @param defaultFail 缺省的失败代码
     */
    public static void setDefaultFail(int defaultFail) {
        DEFAULT_FAIL = defaultFail;
    }

    public static int getDefaultSuccess() {
        return DEFAULT_SUCCESS;
    }

    public static void setDefaultSuccess(int defaultSuccess) {
        Result.DEFAULT_SUCCESS = defaultSuccess;
    }

    public static Result success() {
        return new Result();
    }

    public static Result success(String message) {
        return new Result(DEFAULT_SUCCESS, message);
    }

    public static Result fail(String message) {
        return fail(DEFAULT_FAIL, message, null);
    }

    public static Result fail(int resultCode, String message) {
        return fail(resultCode, message, null);
    }

    public static Result fail(int resultCode, String message, Throwable exception) {
        if (resultCode == DEFAULT_SUCCESS) {
            throw new IllegalArgumentException("失败的代码不能与 DEFAULT_SUCCESS 相同");
        }
        return new Result(resultCode, message, exception);
    }

    ////////////////////////////////////////////////////////////////

    public Map<String, Object> getData() {
        return data;
    }

    public void setData(Map<String, Object> data) {
        this.data = data;
    }

    /**
     * 设置值
     *
     * @param key   键
     * @param value 值
     *
     * @return Result 对象自身
     */
    public Result set(String key, Object value) {
        if (this.data == null) {
            this.data = new HashMap<>();
        }
        this.data.put(key, value);
        return this;
    }

    /////////////////////////////////////////////////////////////

    /**
     * 获得值
     *
     * @param key 键
     * @param <T> 值类型
     *
     * @return 值，如果没有则返回 null
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key) {
        return this.data == null ? null : (T) this.data.get(key);
    }

    /**
     * 以特定类型获取返回值属性
     *
     * @param key  属性名
     * @param type 属性值
     * @param <T>  属性值类型
     *
     * @return 如果属性所代表的 JsonElement 对象能够被转化成 T 对象的话，则返回一个包含了属性值的 T 对象。
     */
    @SuppressWarnings({"unchecked"})
    public <T> T getObject(String key, Class<T> type) {
        Object value = data.get(key);
        if (value == null) {
            return null;
        }

        if (type == Date.class) {

            if (value instanceof Number || value.toString().matches("\\d+")) {
                return (T) new Date(Long.valueOf(value.toString()));
            }

            try {
                return (T) FORMAT.parse(value.toString());
            } catch (ParseException e) {
                throw new AppServerException("Property \"" + key + "\" cannot be converted to " + type, e);
            }
        } else if (type.isAssignableFrom(value.getClass())) {
            return (T) value;
        } else if (Number.class.isAssignableFrom(type)) {
            try {
                String strValue = value.toString();

                // 如果目标类型为整数，则去掉小数部分
                strValue = removeDecimalsIfNecessary(type, strValue);

                return type.getDeclaredConstructor(new Class<?>[]{String.class}).newInstance(strValue);
            } catch (Exception e) {
                throw new AppServerException("Property \"" + key + "\" cannot be converted to " + type, e);
            }
        } else if (value instanceof JSONObject) {
            return JSON.parseObject(((JSONObject) value).toJSONString(), type);
        } else if (value instanceof JSONArray) {
            return JSON.parseObject(((JSONArray) value).toJSONString(), type);
        }

        throw new AppServerException("Property \"" + key + "\" cannot be converted to " + type);
    }

    public <T> List<T> getList(String key, Class<T> type) {
        Object value = data.get(key);

        if (value == null) {
            return null;
        }

        if (value instanceof List) {
            List list = (List) value;

            // 没有指定要转换的类型
            if (type == null) {
                return (List<T>) list;
            }

            // 列表为空
            if (list.isEmpty() || list.get(0) == null) {
                return list;
            }

            Class elementType = list.get(0).getClass();

            // 元素类型已经匹配，无需转换
            if (type.isAssignableFrom(elementType)) {
                List<T> listOfType = new ArrayList<T>();
                for (Object o : list) {
                    listOfType.add((T) o);
                }
                return listOfType;
            }
        } else if (value instanceof JSONArray) {
            return JSON.parseArray(((JSONArray) value).toJSONString(), type);
        }

        throw new AppServerException("Property \"" + key + "\" is not a list");
    }

    public String getString(String key) {
        return getString(key, "");
    }

    public String getString(String key, String defaultValue) {
        Object value = this.data.get(key);
        return value == null ? defaultValue : value.toString();
    }

    public Integer getInteger(String key) {
        return getObject(key, Integer.class);
    }

    public int getInteger(String key, int defaultValue) {
        Integer value = getInteger(key);
        return value == null ? defaultValue : value;
    }

    public Long getLong(String key) {
        return getObject(key, Long.class);
    }

    public long getLong(String key, long defaultValue) {
        Long value = getLong(key);
        return value == null ? defaultValue : value;
    }

    public Double getDouble(String key) {
        return getObject(key, Double.class);
    }

    public double getDouble(String key, double defaultValue) {
        Double value = getDouble(key);
        return value == null ? defaultValue : value;
    }

    public Date getDate(String key) {
        return getObject(key, Date.class);
    }

    public Date getDate(String key, Date defaultValue) {
        Date value = getDate(key);
        return value == null ? defaultValue : value;
    }

    public Boolean getBoolean(String key) {
        return getObject(key, Boolean.class);
    }

    public boolean getBoolean(String key, boolean defaultValue) {
        Boolean value = getBoolean(key);
        return value == null ? defaultValue : value;
    }

    // 如果 type 为整数，则去掉 strValue 的小数部分
    private <T> String removeDecimalsIfNecessary(Class<T> type, String strValue) {
        int dotIndex = strValue.indexOf(".");
        if ((type == Integer.class || type == Long.class) && dotIndex >= 0) {
            strValue = strValue.substring(0, dotIndex);
        }
        return strValue;
    }

    public int getResultCode() {
        return resultCode;
    }

    public void setResultCode(int resultCode) {
        this.resultCode = resultCode;
    }

    public String getMessage() {
        return message;
    }


    public void setMessage(String message) {
        this.message = message;
    }

    public boolean isSuccess() {
        return this.resultCode == DEFAULT_SUCCESS;
    }

    public Throwable getException() {
        return exception;
    }

    public void setException(Throwable exception) {
        this.exception = exception;
    }
}
